home *** CD-ROM | disk | FTP | other *** search
- ******************************************************************
- * COPYRIGHT (C) 1986 by Donald Krantz and James Stanley
- * - Note: This is a real, live, actual, registered copyright,
- * and should be treated as such. This source code is from
- * the book "68000 Assembly Language", Krantz and Stanley,
- * Addison-Wesley Publishing Company, Reading, MA, 1986.
- *
- * Permission granted by the authors for non-commercial use
- * in programs released to the public domain, as long as this
- * copyright notice remains attached and visible.
- *
- ***************************************************************
- * File Primitive Operations - CP/M-68K
- *
- * All routines interface to CP/M-68K files. FCB and FILE block
- * definitions are contained in FCB.H.
-
- #fcb.h
- .xdef open,close,fgetc,fputc
-
- * Routines: OPEN Opens a file for input or output.
- * Entry: P0.L points to FILE block. P1.L points to
- * null-terminated name string. P2.W = 0
- * for "File must Exist", nonzero
- * for "Create new and erase old".
- * Exit: File opened or D0.W = -1
- *
- * CLOSE Closes a file. Flushes current buffer
- * if dirty.
- * Entry: P0.L points to FILE block.
- * Exit: File closed or D0.W = -1
- *
- * FGETC Returns next char from file.
- * Entry: P0.L points to FILE block.
- * Exit: D0.W = char or -1 on error.
- *
- * FPUTC Writes next char to file.
- * Entry: P0.L points to FILE block.
- * P1.W is the char to write.
- * Exit: D0.W = 0 for success, -1 on failure.
- *
- *****************************************************************
- open:
- link a6,#0 * Set stack frame
- movem.l d1/d2/a0/a1/a2,-(a7)
- bsr parse_name * Parses filename to FCB
- tst.w d0 * Check return from PARSE_NAME
- bmi exit_open * Exit on error
- bsr fillfcb * Set balance of FCB
- move.l 8(a6),d1 * Get FCB addr for CP/M calls
- tst.w 16(a6) * Open new file or old?
- bne make_new * Make new file, erase any old.
- must_exist:
- move.w #$000f,d0 * CP/M OPEN FILE Function Code
- trap #2 * Call CP/M - Open existing file
- bra exit_open * Exit
- make_new:
- move.w #$0013,d0 * CP/M DELETE FILE Function code
- trap #2 * Call CP/M - delete any old file
- move.w #$0016,d0 * CP/M MAKE FILE Function code
- trap #2 * Call CP/M - attempt create
- exit_open:
- ext.w d0 * Convert CP/M return to our type
- movem.l (a7)+,d1/d2/a0/a1/a2
- unlk a6 * Restore caller's stack frame
- rts
- *
- *****************************************************************
- * PARSE_NAME parses a C name string into CP/M-68K FCB. Uses
- * caller's stack frame. Consider this a local procedure to OPEN.
- parse_name:
- move.l 8(a6),a0 * Pick up FCB address
- move.l 12(a6),a1 * Pick up name string
- move.w #10,d0 * Loop counter for blank filling
- blank_loop:
- move.b #' ',1(a0,d0.w) * Blank fill name "backwards"
- dbra d0,blank_loop * Continue for 11 bytes
- *
- cmp.b #':',1(a1) * Check for drivespec
- bne no_drive * No drivespec, jump over
- move.b (a1),(a0) * Transfer drivespec
- and.b #$0f,(a0)+ * Make into 0..16 drivecode
- addq.l #2,a1 * Put pointer past drivespec
- bra name_xfr * Go transfer name
- no_drive:
- clr.b (a0)+ * Set default drivecode
- name_xfr:
- move.w #7,d0 * Start with filename part
- name_loop:
- move.b (a1)+,d2 * Pickup name character
- beq parse_chk * Found a terminal null...
- cmp.b #'.',d2 * Look for delimiter
- beq ext_xfr * Parse extension
- bsr check * Make uppercase & screen chars
- tst.b d1 * D1 = -1 for illegal character
- bmi parse_err * Exit if CHECK found error
- move.b d2,(a0)+ * Put char in FCB
- dbra d0,name_loop * Try again
- * If we're here, 8 filename chars were found. Skip period.
- move.b (a1)+,d2 * Move pointer and check char
- cmp.b #'.',d2 * Double check for period
- beq ext_xfr * OK, let's continue
- bra parse_err * Badly formed name string.
- ext_xfr:
- move.l 8(a6),a0 * Re-sync FCB pointer
- adda #type,a0 * Point at TYPE field
- move.w #2,d0 * Three chars only for filetype
- ext_loop:
- move.b (a1)+,d2 * Pickup filetype char
- beq parse_chk * Found terminal null...
- bsr check * Make uppercase and screen chars
- tst.b d1 * Look for error return
- bmi parse_err * Exit if CHECK found error.
- move.b d2,(a0)+ * Put filetype char into FCB
- dbra d0,ext_loop * Go for next
- * If we're here, 3 filetype chars were found. Check for null.
- tst.b (a1) * Is it null?
- bne parse_err * No, return error
- parse_chk:
- move.l 8(a6),a0 * Get FCB base addr again
- cmp.b #' ',name(a0) * At least one name char?
- beq parse_err * Nope, return error.
- clr.w d0 * "Good" return code
- rts
- parse_err:
- move.w #-1,d0 * "Bad" return code
- rts
- *
- *****************************************************************
- * FILLFCB zeros the non-name portion of the FCB. Local to OPEN.
- fillfcb:
- move.l 8(a6),a0 * Load FCB base address
- adda #extent,a0 * Index past name portion
- move.w #dma_buf-extent-1,d0 * Loop count
- fill_loop:
- clr.b (a0)+ * Load zeros in all FILE fields
- dbra d0,fill_loop * Continue for all bytes in FILE
- tst.w 16(a6) * Is this a write?
- bne fill_exit * Yes, CURCHR is ok as zero.
- move.l 8(a6),a0 * Get FCB address again.
- move.w #128,curchr(a0) * Mark as "Need to read"
- fill_exit:
- rts
- *
- *****************************************************************
- * CHECK converts lowercase to uppercase and screens chars CP/M
- * considers illegal name characters.
- check:
- cmp.b #'a',d2 * Check for lowercase first.
- blt not_lower * It's less than 'a'
- cmp.b #'z',d2 * Less than 'z'?
- bgt not_lower * It's greater than 'z'
- and.b #$5f,d2 * Makes D2.B uppercase
- not_lower:
- move.w #18,d1 * 19 illegal name chars
- lea bad_chars,a2 * Get name array base
- chk_loop:
- cmp.b (a2)+,d2 * Test for bad char
- beq chk_err * Bad char, return error
- dbra d1,chk_loop * Keep trying.
- clr.w d1 * "Good" error return
- rts * "Good" exit
- chk_err:
- move.w #-1,d1 * "Bad" error return
- rts * "Bad" exit
- bad_chars:
- dc.b '.:[]()<>=*&,!|?/;+-'
- even * Force alignment
- *
- *****************************************************************
- * CLOSE Closes a file. Flushes current buffer if dirty.
- close:
- link a6,#0 * Setup stack frame
- movem.l d1/a0/a1,-(a7) * Save caller's registers
- move.l 8(a6),a0 * Pick up FCB base address
- bsr put_eof * Add ASCII EOF (^Z)
- bmi exit_close * Branch if write failed
- move.l d1,a0 * Setup CP/M parameter
- move.w #$0010,d0 * CP/M CLOSE FILE function code
- trap #2 * Close the file
- exit_close:
- ext.w d0 * Convert CP/M return to our type
- movem.l (a7)+,d1/a0/a1 * Restore caller's registers
- unlk a6 * Restore caller's stack frame
- rts
- *
- *****************************************************************
- * PUT_EOF fills the end of the current DMA buffer with ^Z
- put_eof:
- cmp.w #128,curchr(a0) * Check if we need to add another
- bne do_rest * record with EOFs. (jump on NO)
- tst.w dirty(a0) * look for empty record
- beq do_rest * jump if this record empty
- bsr write_buf * Flush current record
- do_rest:
- move.w #127,d0 * Set up number of chars to fill
- sub.w curchr(a0),d0 * less current character count
- adda curchr(a0),a0 * Get address of first fill char
- adda #dma_buf,a0 * Need this too.
- put_loop:
- move.b #$1a,(a0)+ * Insert character
- dbra d0,put_loop * Continue through buffer
- bsr write_buf * Flush filled record
- rts
- *
- *****************************************************************
- * FPUTC puts a character to a disk file.
- fputc:
- link a6,#0 * Freeze stack frame
- movem.l a0/d1,-(a7) * Save caller's registers
- move.l 8(a6),a0 * Grab FCB
- cmp.w #128,curchr(a0) * Check for record full
- bne just_put * Nope, we're OK.
- bsr write_buf * Flush current record
- just_put:
- move.w curchr(a0),d0 * Get current char count
- addq.w #1,curchr(a0) * Bump counter
- move.w #-1,dirty(a0) * Make dirty
- move.b 13(a6),dma_buf(a0,d0.w) * Save character
- movem.l (a7)+,a0/d1 * Restore caller's registers
- unlk a6 * Restore caller's stack frame
- rts
- *
- *****************************************************************
- * WRITE_BUF writes the current record to disk and resets FILE buf
- write_buf:
- move.l 8(a6),d1 * Get FCB base address
- add.l #dma_buf,d1 * Compute DMA buffer address
- move.w #$001a,d0 * CP/M SET DMA ADDRESS Function
- trap #2 * Setup DMA address
- move.l 8(a6),a0 * Get FCB base address
- clr.w dirty(a0) * Mark dirty flag as "clean"
- clr.w curchr(a0) * Set current char pointer to 0
- move.l a0,d1 * Get FCB address for CP/M
- move.w #$0015,d0 * CP/M WRITE SEQUENTIAL Function
- trap #2 * Write record
- ext.w d0 * Convert CP/M return to our type
- rts
- *
- *****************************************************************
- * FGETC returns the next character from a file.
- fgetc:
- link a6,#0 * Establish stack frame
- movem.l d1/a0,-(a7) * Save caller's registers
- move.l 8(a6),a0 * Get FCB address
- cmp.w #128,curchr(a0) * Do we need to read a record?
- blt no_read * No, just get character
- bsr get_rec * Read a record
- tst.w d0 * Was there an error?
- bmi fget_err * Yes, return error to caller
- no_read:
- move.w curchr(a0),d0 * Get current character counter
- addq.w #1,curchr(a0) * Increment current char count
- move.b dma_buf(a0,d0.w),d0 * Load next character
- cmp.b #$1a,d0 * Is it EOF?
- beq fget_err * Yes, return error.
- ext.w d0 * Make into type "int".
- bra fget_exit * And return...
- fget_err:
- move.w #-1,d0 * Indicate error
- fget_exit:
- movem.l (a7)+,d1/a0 * Restore caller's registers
- unlk a6 * Restore caller's stack frame
- rts
- *
- *****************************************************************
- * GET_REC - reads next record sequentially. Local to FGETx.
- get_rec:
- move.l a0,d1 * Get FCB base address for CP/M
- add.l #dma_buf,d1 * Compute DMA buffer address
- move.w #$001a,d0 * CP/M SET DMA ADDRESS Function
- trap #2 * Setup DMA address
- move.l a0,d1 * Get FCB base address for CP/M
- move.w #$0014,d0 * CP/M READ SEQUENTIAL function
- trap #2 * Get the record
- clr.w dirty(a0) * Mark record as "Clean"
- clr.w curchr(a0) * Reset character counter
- tst.b d0 * Look at return code
- bne grec_err * Returned an error...
- rts * Return success
- grec_err:
- move.w #-1,d0 * Load error return
- rts * Return error
- end